//import _ from "lodash"
import findKey from "lodash/findKey";
async function fetchPost(data: object, api: string): Promise<siyuanRes> {
  let SIYUAN_PORT = "http://127.0.0.1:1210";
  if (window) {
    SIYUAN_PORT = window.origin;
  }
  const body = JSON.stringify(data);
  const result = await fetch(`${SIYUAN_PORT}${api}`, {
    //{${window.origin}
    body: body,
    method: "POST",
  });
  let json: siyuanRes = await result.json();
  if (json.code == 1) {
    console.warn("!出现错误，以下是错误信息");
    console.warn(data);
    console.warn(json.msg);
  }
  return json;
}
async function sql(stmt: string): Promise<Block[]> {
  const data = {
    stmt: stmt,
  };
  let json = await fetchPost(data, `/api/query/sql`);
  return json.data;
}
export async function pushErrMsg(msg: string, timeout?: number): Promise<void> {
  let data: {
    msg: string;
    timeout?: number;
  };
  data = { msg: msg };
  if (timeout) {
    data.timeout = timeout;
  }
  await fetchPost(data, "/api/notification/pushErrMsg");
}
export async function getBlockById(
  id: null | undefined | string
): Promise<Block | null> {
  if (!id) {
    return null;
  }
  let blockList = await sql(`SELECT * FROM blocks WHERE id='${id}'`);
  if (blockList.length > 0) {
    return blockList[0];
  }
  return null;
}
export async function getChildrenBlocks(id: string): Promise<Block[]> {
  return await sql(`SELECT * FROM blocks WHERE parent_id='${id}'`);
}

export async function getParentBlock(block: Block): Promise<Block | null> {
  let parent: Block | null;
  if (block.parent_id) {
    parent = await getBlockById(block.parent_id);
    return parent;
  }
  if (block.type == "d") {
    //文档查父级文档
    let pathList = block.path.split("/");
    if (pathList.length > 2)
      return await getBlockById(pathList[pathList.length - 2]);
  }
  // todo 没有父级文档返回box
  return null;
}
export async function getDescendantBlocks(id: string): Promise<Block[]> {
  /*递归查询子级*/
  return await sql(`
  WITH RECURSIVE
    children_of(id,parent_id,root_id,hash,box,path,hpath,name,alias,memo,tag,content,fcontent,markdown,length,type,subtype,ial,sort,created,updated,layer) AS(
      SELECT blocks.*,0  FROM blocks WHERE blocks.id='${id}'
      UNION
      SELECT blocks.*,children_of.layer-1 FROM blocks,children_of WHERE blocks.parent_id=children_of.id LIMIT 500
      )
    SELECT * FROM children_of `);
}
export async function getRefBlocks(id: string) {
  return await sql(`SELECT blocks.*,refs.markdown AS refmarkdown
    FROM blocks JOIN refs ON blocks.id=refs.def_block_id
    WHERE blocks.id IN
    (SELECT def_block_id FROM refs WHERE block_id='${id}')
    AND refs.block_id='${id}'`);
}
export async function getDefBlocks(id: string) {
  return await sql(`SELECT blocks.*,refs.markdown AS refmarkdown
  FROM blocks JOIN refs ON blocks.id=refs.block_id
  WHERE blocks.id IN
  (SELECT block_id FROM refs WHERE def_block_id='${id}') 
  AND refs.def_block_id='${id}'`);
}

interface siyuanRes {
  code: number;
  msg: string;
  data: any;
}

export async function getBlockKramdown(id: string): Promise<string> {
  const data = {
    id: id,
  };
  const body = JSON.stringify(data);
  const result = await fetch(
    "http://127.0.0.1:6806/api/block/getBlockKramdown",
    {
      body: body,
      method: "POST",
    }
  );
  let json = await result.json();
  return json.data.kramdown;
}
export async function renderSprig(template: string) {
  const data = {
    template: template,
  };
  const body = JSON.stringify(data);
  const result = await fetch("http://127.0.0.1:6806/api/template/renderSprig", {
    body: body,
    method: "POST",
  });
  let json = await result.json();
  return json.data;
}
async function getDoc(id: string): Promise<getDocRes> {
  const data = {
    id: id,
    k: "",
    isBacklink: false,
    mode: 0,
    size: 48,
  };
  let json = await fetchPost(data, "/api/filetree/getDoc");
  return json.data;
}
interface getDocRes {
  blockCount: number;
  box: string;
  content: string;
  eof: boolean;
  id: string;
  isBacklinkExpand: boolean;
  isSyncing: boolean;
  mode: number;
  parent2ID: string;
  parentID: string;
  path: string;
  rootID: string;
  scroll: boolean;
  type: string;
}

export interface Block {
  id: string;
  parent_id: string;
  root_id: string;
  hash: string;
  box: string;
  path: string;
  hpath: string;
  name: string;
  alias: string;
  memo: string;
  tag: string;
  content: string;
  fcontent: string;
  markdown: string;
  length: string;
  type: string;
  subtype: string;
  ial: string;
  sort: string;
  created: string;
  updated: string;
  refmarkdown?: string;
  layer?: string; //查询先祖和后代时使用
}

async function getDescendantBlocksSorted(id: string): Promise<Block[]> {
  let dom = await getDom(id);
  let domBlockAttrList: domBlockAttr[] = [];
  if (dom) {
    getDomBlockAttrsFromDom(dom, domBlockAttrList);
  }
  let blockList: Block[] = await getDescendantBlocks(id);
  blockList.sort((a, b) => {
    let indexOfa = findKey(domBlockAttrList, (e: domBlockAttr) => {
      return e.data_node_id == a.id;
    }) as string;
    let indexOfb: string | undefined = findKey(
      domBlockAttrList,
      (e: domBlockAttr) => {
        return e.data_node_id == b.id;
      }
    ) as string;
    return parseInt(indexOfa) - parseInt(indexOfb);
  });
  return blockList;
}

export async function getMarkdownSorted(id: string): Promise<string> {
  let blocks = await getDescendantBlocksSorted(id);
  let markdownList: string[] = [];
  for (let block of blocks) {
    if (block.markdown) {
      markdownList.push(block.markdown);
    }
  }
  return markdownList.join("\n\n");
}
function getDomBlockAttrsFromDom(dom: Element, attrList: domBlockAttr[]) {
  let domBlockAttr: domBlockAttr = {
    data_node_id: dom.getAttribute("data-node-id"),
    data_type: dom.getAttribute("data-type"),
    data_node_index: dom.getAttribute("data-node-index"),
    class: dom.classList,
  };
  if (domBlockAttr.data_node_id) {
    attrList.push(domBlockAttr);
  }
  for (let item of dom.children) {
    getDomBlockAttrsFromDom(item, attrList);
  }
}

interface domBlockAttr {
  data_node_id: string | null;
  data_node_index: string | null;
  data_type: string | null;
  class: DOMTokenList;
}

export async function getDom(
  blockId: string | null
): Promise<HTMLElement | null> {
  if (!blockId) {
    return null;
  }
  //blockId对应的获取dom，含子级
  const req = await getDoc(blockId);
  if (!req) {
    return null;
  }
  let dom = document.createElement("div") as HTMLElement;
  dom.innerHTML = req.content;
  //列表块转换
  let firstChild = dom.firstElementChild as HTMLElement;
  if (firstChild && firstChild.getAttribute("data-type") == "NodeList") {
    dom = firstChild;
  }
  return dom;
}

//\kernel\treenode\node.go(翻转)
export const typeAbbrMap = {
  // 块级元素
  d: "NodeDocument",
  h: "NodeHeading",
  l: "NodeList",
  i: "NodeListItem",
  c: "NodeCodeBlock",
  m: "NodeMathBlock",
  t: "NodeTable",
  b: "NodeBlockquote",
  s: "NodeSuperBlock",
  p: "NodeParagraph",
  html: "NodeHTMLBlock",
  query_embed: "NodeBlockQueryEmbed",
  ial: "NodeKramdownBlockIAL",
  iframe: "NodeIFrame",
  widget: "NodeWidget",
  tb: "NodeThematicBreak",
  video: "NodeVideo",
  audio: "NodeAudio",
  text: "NodeText",
  img: "NodeImage",
  link_text: "NodeLinkText",
  link_dest: "NodeLinkDest",
  textmark: "NodeTextMark",
};

//todo这个函数不能在挂件中使用
//*获取当前光标所在块id，注意不是鼠标指针
export function getFocusNodeId() {
  if (!window) {
    return null;
  }
  let selection = window.getSelection();
  let focusNode = selection?.focusNode;
  if (!focusNode) {
    return null;
  }
  let nodeId: string | null = null;
  let parent = focusNode.parentElement;
  while (!nodeId) {
    if (parent) {
      nodeId = parent.getAttribute("data-node-id");
    } else {
      break;
    }
    if (!nodeId) {
      parent = parent.parentElement;
    }
  }
  return nodeId;
}
